﻿using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.Reflection;
using System.Threading;
using System.Threading.Tasks;

namespace ADSync2.EngineComponents
{
	/// <summary>
	/// Logging class. Asynchronously executes log events.
	/// </summary>
	public class iLog : IDisposable
	{
		#region Properties
		private bool bDisposed;

		//workaround to securely complete initiated Task (ProcessQueue)
		//rewrite when time
		private bool Disposing;

		private ConcurrentQueue<iLogEntry> MessageQueue
		{
			get;
			set;
		}
		private Task TaskAnchor
		{
			get;
			set;
		}
		private EventLog EventLogObject
		{
			get;
			set;
		}
		//private StreamWriter WriterObject
		//{
		//    get;
		//    set;
		//}
		private LoggingModes LoggingMode
		{
			get;
			set;
		}

		private String LogFileName
		{
			get;
			set;
		}
		#endregion

		#region Constructors
		public iLog(LoggingModes LoggingMode)
		{
			try
			{
				this.MessageQueue = new ConcurrentQueue<iLogEntry>();

				this.EventLogObject = new System.Diagnostics.EventLog("Application", ".", System.Reflection.Assembly.GetExecutingAssembly().GetName().Name);

				this.LogFileName = String.Format(@"{0}\{1}-{2}.log",
					Path.GetDirectoryName(new Uri(Assembly.GetExecutingAssembly().CodeBase).LocalPath),
					Path.GetFileNameWithoutExtension(new Uri(Assembly.GetExecutingAssembly().CodeBase).LocalPath),
					DateTime.Now.ToString("yyyyMMdd")
					);


				this.LoggingMode = LoggingMode;

				this.Disposing = false;
				this.TaskAnchor = Task.Factory.StartNew(() => ProcessQueue(), TaskCreationOptions.LongRunning);
			}
			catch (Exception eX)
			{
				throw new Exception(string.Format("{0}::{1}", new StackFrame(0, true).GetMethod().Name, eX.Message));
			}
		}
		public iLog() : this(0x00){}
		#endregion

		#region Public Methods
		public void WriteLog(iLogEntry LogEntry)
		{
			this.MessageQueue.Enqueue(LogEntry);
		}
		#endregion

		#region Private Methods
		private void ProcessQueue()
		{
			try
			{
				while (!this.Disposing || !this.MessageQueue.IsEmpty)
				{
					iLogEntry LogEntry;
					if (this.MessageQueue.TryDequeue(out LogEntry))
					{
						if ((this.LoggingMode & LoggingModes.Verbose) == LoggingModes.Verbose )
						{
							if (LogEntry.EntrySeverity == iLogEntry.LogEntrySeverity.Error)
							{
								if ((this.LoggingMode & LoggingModes.SystemLog) == LoggingModes.SystemLog)
								{
									this.WriteEventLog(LogEntry);
								}
								if ((this.LoggingMode & LoggingModes.File) == LoggingModes.File)
								{
									this.WriteFileLog(LogEntry);
								}
								if ((this.LoggingMode & LoggingModes.Console) == LoggingModes.Console)
								{
									this.WriteConsole(LogEntry);
								}
							}
							if (LogEntry.EntrySeverity == iLogEntry.LogEntrySeverity.Warning)
							{
								if ((this.LoggingMode & LoggingModes.SystemLog) == LoggingModes.SystemLog)
								{
									//this.WriteEventLog(LogEntry);
								}
								if ((this.LoggingMode & LoggingModes.File) == LoggingModes.File)
								{
									this.WriteFileLog(LogEntry);
								}
								if ((this.LoggingMode & LoggingModes.Console) == LoggingModes.Console)
								{
									this.WriteConsole(LogEntry);
								}
							}
							if (LogEntry.EntrySeverity == iLogEntry.LogEntrySeverity.Normal)
							{
								if ((this.LoggingMode & LoggingModes.SystemLog) == LoggingModes.SystemLog)
								{
									//this.WriteEventLog(LogEntry);
								}
								if ((this.LoggingMode & LoggingModes.File) == LoggingModes.File)
								{
									this.WriteFileLog(LogEntry);
								}
								if ((this.LoggingMode & LoggingModes.Console) == LoggingModes.Console)
								{
									this.WriteConsole(LogEntry);
								}
							}
							if (LogEntry.EntrySeverity == iLogEntry.LogEntrySeverity.Verbose)
							{
								if ((this.LoggingMode & LoggingModes.SystemLog) == LoggingModes.SystemLog)
								{
									//this.WriteEventLog(LogEntry);
								}
								if ((this.LoggingMode & LoggingModes.File) == LoggingModes.File)
								{
									this.WriteFileLog(LogEntry);
								}
								if ((this.LoggingMode & LoggingModes.Console) == LoggingModes.Console)
								{
									this.WriteConsole(LogEntry);
								}
							}
						}
						else
						{
							if (LogEntry.EntrySeverity == iLogEntry.LogEntrySeverity.Error)
							{
								if ((this.LoggingMode & LoggingModes.SystemLog) == LoggingModes.SystemLog)
								{
									this.WriteEventLog(LogEntry);
								}
								if ((this.LoggingMode & LoggingModes.File) == LoggingModes.File)
								{
									this.WriteFileLog(LogEntry);
								}
								if ((this.LoggingMode & LoggingModes.Console) == LoggingModes.Console)
								{
									this.WriteConsole(LogEntry);
								}
							}
							if (LogEntry.EntrySeverity == iLogEntry.LogEntrySeverity.Warning)
							{
								if ((this.LoggingMode & LoggingModes.SystemLog) == LoggingModes.SystemLog)
								{
									this.WriteEventLog(LogEntry);
								}
								if ((this.LoggingMode & LoggingModes.File) == LoggingModes.File)
								{
									this.WriteFileLog(LogEntry);
								}
								if ((this.LoggingMode & LoggingModes.Console) == LoggingModes.Console)
								{
									this.WriteConsole(LogEntry);
								}
							}
						}
					}
					//to be rewritten with proper threading techniques
					Thread.Sleep(30);
				}
			}
			catch (Exception eX)
			{
				throw new Exception(string.Format("{0}::{1}",new StackFrame(0,true).GetMethod().Name,eX.Message));
			}
		}

		private void WriteEventLog(iLogEntry LogEntry)
		{
			if (LogEntry.EntrySeverity == iLogEntry.LogEntrySeverity.Error)
			{
				this.EventLogObject.WriteEntry(LogEntry.Message, EventLogEntryType.Error, LogEntry.EventID);
			}
			else if (LogEntry.EntrySeverity == iLogEntry.LogEntrySeverity.Warning)
			{
				this.EventLogObject.WriteEntry(LogEntry.Message, EventLogEntryType.Warning, LogEntry.EventID);
			}
			else if (LogEntry.EntrySeverity == iLogEntry.LogEntrySeverity.Normal)
			{
				this.EventLogObject.WriteEntry(LogEntry.Message, EventLogEntryType.Information, LogEntry.EventID);
			}
			else if (LogEntry.EntrySeverity == iLogEntry.LogEntrySeverity.Verbose)
			{
				this.EventLogObject.WriteEntry(LogEntry.Message, EventLogEntryType.Information, LogEntry.EventID);
			}
		}

		private void WriteFileLog(iLogEntry LogEntry)
		{
			try
			{
				using(StreamWriter WriterObject = new StreamWriter(LogFileName, true))
				{
					WriterObject.WriteLine(
						String.Format("{0}\t{1}\t{2}",
							DateTime.Now.ToString("s"),
							LogEntry.EntrySeverity,
							LogEntry.Message)
						);
				}
			}
			catch
			{
				this.LogFileName = String.Format(@"{0}\{1}-{2}.log",
					Path.GetDirectoryName(new Uri(Assembly.GetExecutingAssembly().CodeBase).LocalPath),
					Path.GetFileNameWithoutExtension(new Uri(Assembly.GetExecutingAssembly().CodeBase).LocalPath),
					DateTime.Now.ToString("yyyyMMddhhmmss")
					);

				using(StreamWriter WriterObject = new StreamWriter(LogFileName, true))
				{
					WriterObject.WriteLine(
						String.Format("{0}\t{1}\t{2}",
							DateTime.Now.ToString("s"),
							LogEntry.EntrySeverity,
							LogEntry.Message)
						);
				}
			}
		}

		private void WriteConsole(iLogEntry LogEntry)
		{
			try
			{
				Console.WriteLine(LogEntry.Message);
			}
			catch
			{
				this.LogFileName = String.Format(@"{0}\{1}-{2}.log",
					Path.GetDirectoryName(new Uri(Assembly.GetExecutingAssembly().CodeBase).LocalPath),
					Path.GetFileNameWithoutExtension(new Uri(Assembly.GetExecutingAssembly().CodeBase).LocalPath),
					DateTime.Now.ToString("yyyyMMddhhmmss")
					);

				using (StreamWriter WriterObject = new StreamWriter(LogFileName, true))
				{
					WriterObject.WriteLine(
						String.Format("{0}\t{1}\t{2}",
							DateTime.Now.ToString("s"),
							LogEntry.EntrySeverity,
							LogEntry.Message)
						);
				}
			}
		}
		#endregion

		#region enum
		public enum LogType
		{
 			File,
			EventLog
		}

		public enum LoggingModes
		{
			Verbose = 0x01,
			//NormalWithFile = 0x01,
			Console = 0x10,
			SystemLog = 0x20,
			File = 0x40
		}
		#endregion

		#region IDisposable Members
		public void Dispose()
		{
			Dispose(true);
			GC.SuppressFinalize(this);
		}

		protected virtual void Dispose(bool bDisposing)
		{
			if (!this.bDisposed)
			{
				if (bDisposing)
				{
					this.Disposing = bDisposing;
					this.TaskAnchor.Wait();
					this.TaskAnchor.Dispose();
					this.EventLogObject.Dispose();
				}

				this.bDisposed = true;
			}
		}

		#endregion
	}
}